package name.cyrax.red.jpv.impl.funcs.asm;

import java.util.AbstractSequentialList;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import name.cyrax.red.jpv.api.adapters.NumericAdapter;

public class NumericAdapterVisitor extends AbstractSequentialList<NumericAdapter> 
{
	private final VisitedEntry rootEntry;
	private VisitedEntry tailEntry;
	private int size;
	
	public NumericAdapterVisitor(NumericAdapter root) {
		this.rootEntry = new VisitedEntry(root, null, null);
		tailEntry = rootEntry;
		size = 1;
		collect(rootEntry);
	}
	
	private void collect(VisitedEntry startFrom) {
		while (startFrom != null) {
			NumericAdapter adapter = startFrom.adapter;
			
			int idxParam = 0;
			while (true) {
				NumericAdapter p = adapter.getParam(idxParam);
				if (p == null) break;
				addEntry(p);
				idxParam++;
			}
			startFrom = startFrom.next;
		};
	}

	private VisitedEntry addEntry(NumericAdapter adapter) {
		VisitedEntry result = new VisitedEntry(adapter, null, tailEntry);
		tailEntry.next = result;
		tailEntry = result;
		size++;
		return result;
	}
	
	private final class VisitedEntry {
		public final int seqNo;
		public final NumericAdapter adapter;
		public VisitedEntry next; //away from root
		public final VisitedEntry prev; //back to root
//		public int reuseCount;
		
		public VisitedEntry(NumericAdapter adapter, VisitedEntry next, VisitedEntry prev) {
			this.seqNo = prev == null ? 0 : prev.seqNo + 1;
			this.adapter = adapter;
			this.next = next;
			this.prev = prev;
		}
	}

//    /**
//     * Tells if the argument is the index of an existing element.
//     */
//    private boolean isElementIndex(int index) {
//        return index >= 0 && index < size;
//    }

    /**
     * Tells if the argument is the index of a valid position for an
     * iterator or an add operation.
     */
    private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
    }

    /**
     * Constructs an IndexOutOfBoundsException detail message.
     * Of the many possible refactorings of the error handling code,
     * this "outlining" performs best with both server and client VMs.
     */
    private String outOfBoundsMsg(int index) {
        return "Index: "+index+", Size: "+size;
    }

//    private void checkElementIndex(int index) {
//        if (!isElementIndex(index))
//            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
//    }

    private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
	
	@Override public ListIterator<NumericAdapter> listIterator(final int startIndex)
	{
		checkPositionIndex(startIndex);
		
		return new ListIterator<NumericAdapter>() {
			private VisitedEntry current;
			{
				if (startIndex == size)
					current = null;
				else if (startIndex <= size / 2) {
					current = rootEntry;
					while (current.seqNo < startIndex) {
						current = current.next;
					}
				} else {
					current = tailEntry;
					while (current.seqNo > startIndex) {
						current = current.prev;
					}
				}
			}

			@Override public boolean hasNext()
			{
				return current != null && current.next != null;
			}

			@Override public NumericAdapter next()
			{
				if (current == null)
					throw new NoSuchElementException();
				NumericAdapter result = current.adapter;
				current = current.next;
				return result;
			}

			@Override public boolean hasPrevious()
			{
				return current == null || current.prev != null;
			}

			@Override public NumericAdapter previous()
			{
				if (current == null)
					current = tailEntry;
				else {
					VisitedEntry p = current.prev;
					if (p == null) throw new NoSuchElementException();
					current = p;
				}
				return current.adapter;
			}

			@Override public int nextIndex()
			{
				if (current == null) return size;
				return current.seqNo;
			}

			@Override public int previousIndex()
			{
				if (current == null) return tailEntry.seqNo;
				VisitedEntry p = current.prev;
				if (p == null) return -1;
				return p.seqNo;
			}

			@Override public void remove()
			{
				throw new UnsupportedOperationException();
			}

			@Override public void set(NumericAdapter e)
			{
				throw new UnsupportedOperationException();
			}

			@Override public void add(NumericAdapter e)
			{
				throw new UnsupportedOperationException();
			}			
		};
	}

	@Override public int size()
	{
		return size;
	}
}

